--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 113ececd7adcccff41cb2266117dda23c6dac6f5
Parents : 05961f1
Author : Sudo-Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-01-01T23:36:34-06:00
feat(voicemail): implement greeting recording functionality with stabilization delay and improved handling of active calls
Changes
Diff
diff --git a/meshchatx/src/backend/voicemail_manager.py b/meshchatx/src/backend/voicemail_manager.py
index 662988b4..0e1b6362 100644
--- a/meshchatx/src/backend/voicemail_manager.py
+++ b/meshchatx/src/backend/voicemail_manager.py
@@ -28,6 +28,7 @@ class VoicemailManager:
os.makedirs(self.recordings_dir, exist_ok=True)
self.is_recording = False
+ self.is_greeting_recording = False
self.recording_pipeline = None
self.recording_sink = None
self.recording_start_time = None
@@ -36,6 +37,9 @@ class VoicemailManager:
self.on_new_voicemail_callback = None
+ # stabilization delay for voicemail greeting
+ self.STABILIZATION_DELAY = 2.5
+
# Paths to executables
self.espeak_path = self._find_espeak()
self.ffmpeg_path = self._find_ffmpeg()
@@ -268,6 +272,20 @@ class VoicemailManager:
def session_job():
try:
+ # Wait for link to stabilize
+ RNS.log(
+ f"Voicemail: Waiting {self.STABILIZATION_DELAY}s for link stabilization...",
+ RNS.LOG_DEBUG,
+ )
+ time.sleep(self.STABILIZATION_DELAY)
+
+ if not telephone.active_call:
+ RNS.log(
+ "Voicemail: Call ended during stabilization delay",
+ RNS.LOG_DEBUG,
+ )
+ return
+
# 1. Play greeting
if os.path.exists(greeting_path):
try:
@@ -286,6 +304,7 @@ class VoicemailManager:
while greeting_source.running:
time.sleep(0.1)
if not telephone.active_call:
+ greeting_pipeline.stop()
return
greeting_pipeline.stop()
@@ -297,6 +316,9 @@ class VoicemailManager:
else:
RNS.log("Voicemail: No greeting available to play", RNS.LOG_WARNING)
+ if not telephone.active_call:
+ return
+
# 2. Play beep
beep_source = LXST.ToneSource(
frequency=800,
@@ -309,6 +331,9 @@ class VoicemailManager:
time.sleep(0.5)
beep_source.stop()
+ if not telephone.active_call:
+ return
+
# 3. Start recording
self.start_recording(caller_identity)
@@ -410,6 +435,7 @@ class VoicemailManager:
os.remove(filepath)
self.is_recording = False
+ self.is_greeting_recording = False
self.recording_start_time = None
self.recording_remote_identity = None
self.recording_filename = None
@@ -417,3 +443,53 @@ class VoicemailManager:
except Exception as e:
RNS.log(f"Error stopping recording: {e}", RNS.LOG_ERROR)
self.is_recording = False
+
+ def start_greeting_recording(self):
+ telephone = self.telephone_manager.telephone
+ if not telephone:
+ return
+
+ # Ensure we have audio input
+ if not telephone.audio_input:
+ RNS.log(
+ "Voicemail: No audio input available for recording greeting",
+ RNS.LOG_ERROR,
+ )
+ return
+
+ temp_wav = os.path.join(self.greetings_dir, "temp_greeting.wav")
+ if os.path.exists(temp_wav):
+ os.remove(temp_wav)
+
+ try:
+ self.greeting_recording_sink = OpusFileSink(
+ os.path.join(self.greetings_dir, "greeting.opus")
+ )
+ self.greeting_recording_sink.samplerate = 48000
+
+ self.greeting_recording_pipeline = Pipeline(
+ source=telephone.audio_input,
+ codec=Null(),
+ sink=self.greeting_recording_sink,
+ )
+ self.greeting_recording_pipeline.start()
+ self.is_greeting_recording = True
+ RNS.log("Voicemail: Started recording greeting from mic", RNS.LOG_DEBUG)
+ except Exception as e:
+ RNS.log(
+ f"Voicemail: Failed to start greeting recording: {e}", RNS.LOG_ERROR
+ )
+
+ def stop_greeting_recording(self):
+ if not self.is_greeting_recording:
+ return
+
+ try:
+ self.greeting_recording_pipeline.stop()
+ self.greeting_recording_sink = None
+ self.greeting_recording_pipeline = None
+ self.is_greeting_recording = False
+ RNS.log("Voicemail: Stopped recording greeting from mic", RNS.LOG_DEBUG)
+ except Exception as e:
+ RNS.log(f"Voicemail: Error stopping greeting recording: {e}", RNS.LOG_ERROR)
+ self.is_greeting_recording = False
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────